#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "user_define.h"
#include "tcm_constants.h"
#include "tcm_structures.h"
#include "tcmfunc.h"
#include "tcm_cube_struct.h"
#include "tcm_key_manage.h"
#include "tcm_key_desc.h"
#include "tcm_pik_desc.h"

#include "system_entity.h"
#include "integrity_measure.h"

#include "key_manage_pm.h"
// add para lib_include

RECORD(TCM_KEY_DESC,LOCAL_KEYSET) * local_keyset_get (char * name);
RECORD(TCM_KEY_DESC,REMOTE_KEYSET) * remote_keyset_get (BYTE *uuid,char * name);

BYTE external_data_store[DIGEST_SIZE];

int count=0;

int key_manage_pm_init(void * sub_proc, void * para)
{
	int ret;
	// add yorself's module init func here
	return 0;
}
int key_manage_pm_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		if((type==TYPE(GENERAL_RETURN))&&(subtype==SUBTYPE(GENERAL_RETURN,STRING)))
		{
			ret=proc_keymanage_cmd(sub_proc,recv_msg);
		}
        else
        {
	        ret=ex_module_sendmsg(sub_proc,recv_msg);
        }
	}
	return 0;
}

int proc_keymanage_cmd(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(GENERAL_RETURN,STRING) * keymanage_cmd;
	ret=message_get_record(recv_msg,&keymanage_cmd,0);
	if(ret<0)
		return ret;
    printf("key_manage_pm:enter the keymanage cmd!\n");

    if(Strcmp(keymanage_cmd->name,"localkeyset_gen")==0)
        return proc_keymanage_return(sub_proc,keymanage_cmd,recv_msg);
    else if(Strcmp(keymanage_cmd->name,"remotekey_send")==0)
        return proc_keymanage_keysend(sub_proc,keymanage_cmd,recv_msg);
    else if(Strcmp(keymanage_cmd->name,"pcrquote")==0)
        return proc_keymanage_pcrquote(sub_proc,keymanage_cmd,recv_msg);
    return ex_module_sendmsg(sub_proc,recv_msg);
}
	
int proc_keymanage_return(void * sub_proc,RECORD(GENERAL_RETURN,STRING) * keymanage_cmd,void * recv_msg)
{
	int ret;
	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
	char user_name[DIGEST_SIZE];
	void * new_msg;
    MSG_EXPAND * msg_expand;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	

    
    print_cubeaudit("enter keymanage_return function!");

	ret = message_remove_expand(recv_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),&msg_expand);
	if(ret<0)
		return ret;
	if(msg_expand==NULL)
		return -ENOMEM;
    instance_info=msg_expand->expand;
    if(instance_info==NULL)
        return -EINVAL;
	
    printf("key_manage_pm: proc_keymanage_return  get instance_info\n");
	proc_share_data_getvalue("uuid",local_uuid);
    if(Memcmp(local_uuid,instance_info->node_uuid,DIGEST_SIZE)!=0)
        return -EINVAL;
    Memset(instance_info->node_uuid,0,DIGEST_SIZE);

    print_cubeaudit("user name is %s!",instance_info->user_name);
	//proc_share_data_getvalue("proc_name",instance_info->proc_name);

	new_msg=message_create(TYPE_PAIR(MESSAGE,INSTANCE_INFO),recv_msg);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,instance_info);
	if(ret<0)
		return ret;
	
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}

int proc_keymanage_keysend(void * sub_proc,RECORD(GENERAL_RETURN,STRING) * keymanage_cmd,void * recv_msg)
{
	int ret;
	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
	char user_name[DIGEST_SIZE];
	void * new_msg;
    MSG_EXPAND * msg_expand;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	

    ret = message_get_define_expand(recv_msg,&msg_expand,TYPE_PAIR(MESSAGE,INSTANCE_INFO));
	if(ret<0)
        return ret;
    if(msg_expand==NULL)
        return 0;
    instance_info=msg_expand->expand;

	new_msg=message_create(TYPE_PAIR(MESSAGE,INSTANCE_INFO),recv_msg);	
//	ret=message_add_expand_data(recv_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),instance_info);	
//	if(ret<0)
//		return ret;
	ret=message_add_record(new_msg,instance_info);
	if(ret<0)
		return ret;	
	ret=ex_module_sendmsg(sub_proc,new_msg);
    printf("key_manage_pm: proc_keymanage_keysend get instance_info\n");
	return ret;
}

int proc_keymanage_pcrquote(void * sub_proc,RECORD(GENERAL_RETURN,STRING) * keymanage_cmd,void * recv_msg)
{
	int ret;
	RECORD(TCM_PIK_DESC,PCRQUOTE) * quote_report;
	char user_name[DIGEST_SIZE];
	void * new_msg;
    	MSG_EXPAND * msg_expand;
	DB_RECORD *db_record;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	


    ret = message_get_define_expand(recv_msg,&msg_expand,TYPE_PAIR(TCM_PIK_DESC,PCRQUOTE));
    if(ret<0)
        return ret;
        // check if we get quote report 
    if(msg_expand==NULL)
    {
        printf("key_manage_pm: proc_keymanage_pcrquote no pcrquote!\n");
        ret = message_get_define_expand(recv_msg,&msg_expand,TYPE_PAIR(TCM_KEY_DESC,VERIFY_DESC));
        // check if we get verify result 
        if(msg_expand == NULL)
        {
            printf("key_manage_pm: proc_keymanage_pcrquote no verify result!\n");
            // check if there exists random data 
            ret = message_remove_expand(recv_msg,TYPE_PAIR(GENERAL_RETURN,UUID),&msg_expand);
            if(msg_expand == NULL)
            {
                printf("key_manage_pm: proc_keymanage_pcrquote no random data!\n");
                // generate external random data from (USER_DEFINE,RANDOM)
                RECORD(GENERAL_RETURN,UUID) * external_data;
                external_data=Talloc0(sizeof(*external_data));
                external_data->name=dup_str("pcrquote",0);
                RAND_bytes(external_data->return_value,DIGEST_SIZE);

               	Memcpy(external_data_store,external_data->return_value,DIGEST_SIZE);
               	ret = message_add_expand_data(recv_msg,TYPE_PAIR(GENERAL_RETURN,UUID),external_data);
	           	ret=ex_module_sendmsg(sub_proc,recv_msg);
           	}
            else
	// general quote_report from (TCM_PIK_DESC,QUOTE_REPORT)
            {
                printf("key_manage_pm: proc_keymanage_pcrquote has random data!\n");
                
               	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
               	RECORD(TCM_PIK_DESC,PCRQUOTE) * quote_report;
               	// get qupte nonce value 
               	printf("key_manage_pm: enter quote report get process!\n");
                quote_report = Talloc0(sizeof(*quote_report));
                if(quote_report == NULL)
                    return -ENOMEM;
                // duplicate nonce
                RECORD(GENERAL_RETURN,UUID) * quote_nonce = msg_expand->expand;
                Memcpy(quote_report->nonce,quote_nonce->return_value,DIGEST_SIZE);
                // get (MESSAGE,INSTANCE_INFO) value
            
                ret = message_remove_expand(recv_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),&msg_expand);
                if(ret<0)
                    return ret;
                instance_info=msg_expand->expand;
            
                //duplicate (TCM_PIK_DESC,USERINFO) value
                Strncpy(quote_report->userinfo.username,instance_info->user_name,DIGEST_SIZE);
                Memcpy(quote_report->userinfo.node_uuid,instance_info->node_uuid,DIGEST_SIZE);
                quote_report->userinfo.describe=dup_str("quote_report test",0);

               	ret = message_add_expand_data(recv_msg,TYPE_PAIR(TCM_PIK_DESC,PCRQUOTE),quote_report);
            	ret=ex_module_sendmsg(sub_proc,recv_msg);
               	return ret;  
	        }                           
        }
        else
        {
                printf("key_manage_pm: proc_keymanage_pcrquote has verify result!\n");
                // prepare to output results
                RECORD(TCM_KEY_DESC,VERIFY_DESC) * verify_desc;
                RECORD(TCM_PIK_DESC,PCRQUOTE) * quote_report;
                verify_desc=msg_expand->expand;

                DB_RECORD * db_record;
                
                db_record = memdb_find(verify_desc->object_uuid,TYPE_PAIR(TCM_PIK_DESC,PCRQUOTE));
          
                if(db_record==NULL)
                    return -EINVAL;
                printf("server: get verified report, verify result is %d!\n",verify_desc->result);
    
                printf("server: verified pcr value is :\n");
                quote_report = db_record->record;
                print_bin_data(quote_report->pcrComp.pcrValue,32,16);
       
                // build pcr_collect struct
                RECORD(INTEGRITY_COLLECT,PCR_MEASURE) * pcr_collect;
                pcr_collect = Talloc0(sizeof(*pcr_collect));
                if(pcr_collect == NULL)
                    return -ENOMEM;

                Memcpy(pcr_collect->node_uuid,quote_report->userinfo.node_uuid,DIGEST_SIZE);

                RECORD(GENERAL_RETURN,STRING) * pcr_choice;
                ret = message_get_record(recv_msg,&pcr_choice,0);
                if(ret<0)
                    return -EINVAL;
                pcr_collect->pcr_index = Atoi(pcr_choice->return_value,16);
                Memcpy(pcr_collect->pcr_value,quote_report->pcrComp.pcrValue,DIGEST_SIZE);

                //  prepare pcr_verify struct 
                RECORD(SYSTEM_ENTITY,PCR) * pcr_verify;
                pcr_verify = Talloc0(sizeof(*pcr_verify));
                if(pcr_verify == NULL)
                    return -ENOMEM;
                Memcpy(pcr_verify->node_uuid,pcr_collect->node_uuid,DIGEST_SIZE);
                pcr_verify->pcr_index=pcr_collect->pcr_index;

                // if pcr verify failed, return verify fail result
                if(verify_desc->result!=0)
                {
                    printf("key_manag_pm: if verify failed return verify fail result:\n");
                    pcr_verify->verify_result = verify_desc->result;
                    message_add_expand_data(recv_msg,TYPE_PAIR(SYSTEM_ENTITY,PCR),pcr_verify);
	                ret=ex_module_sendmsg(sub_proc,recv_msg);
                    return ret;
                }
                
                // check if there is pcr measure result
                RECORD(INTEGRITY_COLLECT,PCR_MEASURE) * pcr_measure;
                BYTE comp_uuid[DIGEST_SIZE];

                printf("key_manag_pm: compute pcr_collect index:\n");
                ret = memdb_comp_record_uuid(pcr_collect,TYPE_PAIR(INTEGRITY_COLLECT,PCR_MEASURE),comp_uuid);
                if(ret<0)
                {
                    printf("key_manag_pm: compute pcr_collect index failed!:\n");
                    return -EINVAL;
                }
                db_record=memdb_find(comp_uuid,TYPE_PAIR(INTEGRITY_COLLECT,PCR_MEASURE));
                if(db_record == NULL)
                    // first time, store the result 
                {   
                        printf("key_manag_pm: can't find this node's pcr_collect value \n");
                        ret = memdb_store(pcr_collect,TYPE_PAIR(INTEGRITY_COLLECT,PCR_MEASURE),NULL);
                        printf("key_manag_pm: store pcr_collect value return %d \n",ret);
                }
                else
                {
                    // first time collect this node's pcr_index
                    pcr_measure = db_record->record;
                    ret = Memcmp(pcr_measure->pcr_value,pcr_collect->pcr_value,DIGEST_SIZE);
                    if(ret == 0)
                        pcr_verify->verify_result=1;
                    else
                        pcr_verify->verify_result =2; 
                }

		switch(pcr_verify->verify_result)
		{
			case 0 :
				if(pcr_verify->pcr_index == 8)
            				printf("boot pcr value registed!\n");
				else if(pcr_verify->pcr_index == 14)
            				printf("dynamic measure pcr value registed!\n");
				break;
			case 1 :
            			printf("pcr %d collect value is same as registered value!\n",pcr_verify->pcr_index);
				break;
			case 2 :
            			printf("pcr %d collect value is different as registered value!\n",pcr_verify->pcr_index);
				break;
			default :
            			printf("pcr %d report error! err code %d!\n",pcr_verify->pcr_index,pcr_verify->verify_result);
				break;
		} 
			
                message_add_expand_data(recv_msg,TYPE_PAIR(SYSTEM_ENTITY,PCR),pcr_verify);
	        ret=ex_module_sendmsg(sub_proc,recv_msg);
                return ret;
        }
    }
    
    else
    {
        // prepare to verify quote report
        printf("key_manage_pm: proc_keymanage_pcrquote has pcr report!\n");

        RECORD(TCM_PIK_DESC,PCRQUOTE) * quote_report;
        quote_report=msg_expand->expand;
        DB_RECORD * db_record;
        db_record = memdb_store(quote_report,TYPE_PAIR(TCM_PIK_DESC,PCRQUOTE),NULL);
        if(db_record == NULL)
        {
            printf("key_manage_pm : store quote report failed!\n");

        }
          
        print_bin_data(db_record->head.uuid,32,16);  

	    ret=ex_module_sendmsg(sub_proc,recv_msg);
    }
       
    
	return ret;
}

RECORD(TCM_KEY_DESC,LOCAL_KEYSET) * local_keyset_get (char * name)
{
	int ret;
	RECORD(TCM_KEY_DESC,LOCAL_KEYSET ) * local_keyset;
	DB_RECORD * db_record;
	char buf[DIGEST_SIZE];
	int i;
	db_record=memdb_find_first(TYPE_PAIR(TCM_KEY_DESC,LOCAL_KEYSET),"user_name",name);
	if(db_record==NULL)
		return NULL;
	return db_record->record;
}

RECORD(TCM_KEY_DESC,REMOTE_KEYSET) * remote_keyset_get (BYTE *uuid,char * name)
{
	int ret;
	RECORD(TCM_KEY_DESC,REMOTE_KEYSET ) * remote_keyset;
	DB_RECORD * db_record;
	char buf[DIGEST_SIZE];
	int i;
	remote_keyset=memdb_get_first_record(TYPE_PAIR(TCM_KEY_DESC,REMOTE_KEYSET));
	while(remote_keyset!=NULL)
	{
		ret=0;
		if(uuid!=NULL)
		{
			if(Memcmp(uuid,remote_keyset->node_uuid,DIGEST_SIZE)==0)
				ret=1;
			else
				ret=-1;
		}
		if(name!=NULL)
		{
			if(Strncmp(name,remote_keyset->user_name,DIGEST_SIZE)==0)
			{
				if(uuid==NULL)
					ret=1;
			}
			else
				ret=0;

		}
		if(ret==1)
			break;
		remote_keyset=memdb_get_next_record(TYPE_PAIR(TCM_KEY_DESC,REMOTE_KEYSET));
		
	}

	return remote_keyset;
}
